package com.pi.shisan.service.service.impl.next;

import com.pi.shisan.service.annotation.CalcNextTimeType;
import com.pi.shisan.service.constants.ClockTypeEnum;
import com.pi.shisan.service.dto.ClockInfoDTO;
import com.pi.shisan.service.exception.BusinessException;
import com.pi.shisan.service.service.ClockNextTimeService;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Objects;

import static com.pi.shisan.service.util.DateUtil.checkDay;

@CalcNextTimeType(ClockTypeEnum.DIY)
public class DiyNextTimeServiceImpl implements ClockNextTimeService {
    @Override
    public Long calc(ClockInfoDTO clockInfoDTO) {
        Integer year = clockInfoDTO.getClkYear();
        Integer month = clockInfoDTO.getClkMonth();
        Integer day = clockInfoDTO.getClkDay();
        Integer hour = clockInfoDTO.getClkHour();
        Integer minute = clockInfoDTO.getClkMinute();
        Integer week = clockInfoDTO.getClkWeek();
        if (day != null && week != null) {
            throw new BusinessException("周和日不能同时指定");
        }
        LocalDateTime startTimeDate;
        LocalDateTime now = LocalDateTime.now();
        year = year == null ? now.getYear() : year;
        month = month == null ? now.getMonthValue() : month;
        hour = hour == null ? now.getHour() : hour;
        if (day != null) {
            if (day >= 1 && day <= 31) {
                while (!checkDay(year,month,day)) {
                    if (clockInfoDTO.getClkMonth() != null) {
                        if (
                                (Objects.equals(clockInfoDTO.getClkMonth(), 4)
                                        || Objects.equals(clockInfoDTO.getClkMonth(), 6)
                                        || Objects.equals(clockInfoDTO.getClkMonth(), 9)
                                        || Objects.equals(clockInfoDTO.getClkMonth(), 11))
                                        && day > 30
                        ) {
                            throw new BusinessException("此闹铃无效，31号条件将永远无法达成");
                        }
                        if (Objects.equals(clockInfoDTO.getClkMonth(), 2)) {
                            if (day > 29 || clockInfoDTO.getClkYear() != null) {
                                throw new BusinessException("此闹铃无效，"+day+"号条件将永远无法达成");
                            }
                        }
                        if (clockInfoDTO.getClkYear() == null) {
                            year++;
                            continue;
                        }
                        throw new BusinessException("此闹铃可能是无效的");
                    }
                    month ++;
                    if (month > 12) {
                        month = 1;
                        year ++;
                    }
                    if (clockInfoDTO.getClkYear() != null && !Objects.equals(clockInfoDTO.getClkYear(), year)) {
                        throw new BusinessException("此闹铃可能是无效的");
                    }
                }
            }
            startTimeDate = LocalDateTime.of(year,month,day,hour,minute,0);
        } else {
            day = now.getDayOfMonth();
            week = week == null ? now.getDayOfWeek().getValue() : week;
            startTimeDate = LocalDateTime.of(year,month,day,hour, minute,0);
            int currentWeek = startTimeDate.getDayOfWeek().getValue();
            if (!Objects.equals(currentWeek,week)) {
                //3  5
                startTimeDate = startTimeDate.plusDays(week - currentWeek);
            }
        }
        // year month week day hour minute
        //      1      5              0
        // 2022 4      6         11   0

        //                 1          12
        // 2022 4          1     11   12
        LocalDateTime resultTime = startTimeDate;
        if (startTimeDate.isBefore(now)) {
            resultTime = null;
            LocalDateTime tmp = null;
            if (clockInfoDTO.getClkHour() == null) {
                tmp = startTimeDate.plusHours(1).withMinute(minute);
                if (clockInfoDTO.getClkWeek() != null) {
                    int w = checkWeekAddDays(tmp, clockInfoDTO.getClkWeek());
                    if (w > 0) {
                        tmp = tmp.plusDays(w);
                    }
                }
                if (tmp.isAfter(now) && checkCouldUse(tmp, clockInfoDTO)) {
                    resultTime = tmp;
                }
            }

            if (resultTime == null) {
                if (clockInfoDTO.getClkWeek() != null) {
                    tmp = startTimeDate.plusWeeks(1);
                    if (clockInfoDTO.getClkHour() == null) {
                        tmp = tmp.withHour(0);
                    }
                    if (tmp.isAfter(now) && checkCouldUse(tmp, clockInfoDTO)) {
                        resultTime = tmp;
                    }
                } else if (clockInfoDTO.getClkDay() == null){
                    tmp = startTimeDate.plusDays(1);
                    if (clockInfoDTO.getClkHour() == null) {
                        tmp = tmp.withHour(0);
                    }
                    if (tmp.isAfter(now) && checkCouldUse(tmp, clockInfoDTO)) {
                        resultTime = tmp;
                    }
                }
            }

            if (resultTime == null && clockInfoDTO.getClkMonth() == null) {
                tmp = startTimeDate.plusMonths(1);
                if (clockInfoDTO.getClkWeek() != null) {
                    tmp = tmp.withDayOfMonth(1);
                    int w = checkWeekAddDays(tmp, clockInfoDTO.getClkWeek());
                    if (w > 0) {
                        tmp = tmp.plusDays(w);
                    }
                } else if (clockInfoDTO.getClkDay() == null) {
                    tmp = tmp.withDayOfMonth(1);
                }
                if (clockInfoDTO.getClkHour() == null) {
                    tmp = tmp.withHour(0);
                }
                if (tmp.isAfter(now) && checkCouldUse(tmp, clockInfoDTO)) {
                    resultTime = tmp;
                }
            }

            if (resultTime == null && clockInfoDTO.getClkYear() == null) {
                tmp = startTimeDate.plusYears(1);
                if (clockInfoDTO.getClkHour() == null) {
                    tmp = tmp.withHour(0);
                }
                if (clockInfoDTO.getClkMonth() == null) {
                    tmp = tmp.withMonth(1);
                }
                if (clockInfoDTO.getClkWeek() != null) {
                    tmp = tmp.withDayOfMonth(1);
                    int w = checkWeekAddDays(tmp, clockInfoDTO.getClkWeek());
                    if (w > 0) {
                        tmp = tmp.plusDays(w);
                    }
                } else if (clockInfoDTO.getClkDay() == null) {
                    tmp = tmp.withDayOfMonth(1);
                }
                if (tmp.isAfter(now) && checkCouldUse(tmp, clockInfoDTO)) {
                    resultTime = tmp;
                }
            }

        }
        if (resultTime == null || resultTime.isBefore(now)) {
            throw new BusinessException("此闹铃可能是无效的");
        }
        return resultTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    private int checkWeekAddDays(LocalDateTime currTime, int targetWeek) {
        int w = targetWeek - currTime.getDayOfWeek().getValue();
        if (w == 0) {
            return 0;
        }
        return w > 0 ? w : 7 + w;
    }

    private boolean checkCouldUse(LocalDateTime date, ClockInfoDTO clockInfoDTO) {
        if (clockInfoDTO.getClkYear() != null && !Objects.equals(date.getYear(), clockInfoDTO.getClkYear())) {
            return false;
        }
        if (clockInfoDTO.getClkMonth() != null && !Objects.equals(date.getMonthValue(), clockInfoDTO.getClkMonth())) {
            return false;
        }
        if (clockInfoDTO.getClkWeek() != null && !Objects.equals(date.getDayOfWeek().getValue(), clockInfoDTO.getClkWeek())) {
            return false;
        }
        if (clockInfoDTO.getClkDay() != null && !Objects.equals(date.getDayOfMonth(), clockInfoDTO.getClkDay())) {
            return false;
        }
        if (clockInfoDTO.getClkHour() != null && !Objects.equals(date.getHour(), clockInfoDTO.getClkHour())) {
            return false;
        }
        return true;
    }


}
